#include "vm/page.h"
#include "threads/malloc.h"
#include "userprog/pagedir.h"
#include "threads/vaddr.h"

/* Hash function for the supplemental page table. */
unsigned page_hash(const struct hash_elem *e, void *aux UNUSED) {
    struct spte *entry = hash_entry(e, struct spte, hash_elem);
    return hash_bytes(&entry->upage, sizeof entry->upage);
}

/* Comparison function for the supplemental page table. */
bool page_less(const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED) {
    struct spte *entry_a = hash_entry(a, struct spte, hash_elem);
    struct spte *entry_b = hash_entry(b, struct spte, hash_elem);
    return entry_a->upage < entry_b->upage;
}

/* Initializes a supplemental page table. */
void supplemental_page_table_init(struct supplemental_page_table *spt) {
    hash_init(&spt->page_hash, page_hash, page_less, NULL);
}

/* Inserts a page into the supplemental page table. */
bool supplemental_page_table_insert(struct supplemental_page_table *spt, void *upage, void *kpage, bool writable) {
    struct spte *entry = malloc(sizeof(struct spte));
    if (entry == NULL)
        return false;

    entry->upage = upage;
    entry->kpage = kpage;
    entry->location = MEMORY;
    entry->writable = writable;

    struct hash_elem *result = hash_insert(&spt->page_hash, &entry->hash_elem);
    return result == NULL; // Success if no entry was overwritten.
}

/* Finds a page in the supplemental page table. */
struct spte *supplemental_page_table_find(struct supplemental_page_table *spt, void *upage) {
    struct spte entry;
    entry.upage = upage;
    struct hash_elem *e = hash_find(&spt->page_hash, &entry.hash_elem);
    return e != NULL ? hash_entry(e, struct spte, hash_elem) : NULL;
}

/* Removes a page from the supplemental page table. */
void supplemental_page_table_remove(struct supplemental_page_table *spt, void *upage) {
    struct spte entry;
    entry.upage = upage;
    struct hash_elem *e = hash_delete(&spt->page_hash, &entry.hash_elem);

    if (e != NULL) {
        struct spte *spte = hash_entry(e, struct spte, hash_elem
